package com.mojo.concurrent.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Condition示例2
 * <p>
 *
 * @author <a href="mailto:sjj@jianzhimao.com">mojo</a>
 * copyright (C), 2013-2023, 广州九尾信息科技有限公司
 * @date 2023/2/3 10:27
 */
public class ConditionDemo02 {
    public static void main(String[] args) {
        BoundedQueue queue = new BoundedQueue(5);
        int count = 0;
        while (true) {
            ++count;
            final Object val = count;
            Thread t1 = new Thread("线程>>" + val) {

                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    queue.add(val);
                }
            };
            Thread t2 = new Thread("线程<<" + val) {

                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    queue.add(val);
                }
            };
            Thread t3 = new Thread("线程" + val) {

                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    queue.remove();
                }
            };
            t1.start();
            t2.start();
            t3.start();
            try {
                t1.join();
                t2.join();
                t3.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /*
     * 使用Condition实现一个有界队列
     * 有界队列是一种特殊的队列，当队列为空时，队列的获取操作将会阻塞获取线程，直到队列中有新增元素。
     * 当队列已满时，队列的插入操作将会阻塞插入线程，直到队列出现“空位”
     *
     */
    static class BoundedQueue {
        ReentrantLock lock = new ReentrantLock();
        Condition notFull = lock.newCondition();
        Condition notEmpty = lock.newCondition();
        Object[] objects;
        int addIndex, removeIndex, count;

        public BoundedQueue(int size) {
            this.objects = new Object[size];
        }

        public void add(Object obj) {
            /*
             * 1.获取同步状态
             * 2.判断队列是否已满，是则await()否则添加
             * 3.索引
             */
            lock.lock();
            try {
                while (objects.length == count) {
                    notEmpty.await();
                }
                objects[addIndex] = obj;
                ++count;
                System.out.println(Thread.currentThread().getName() + "添加第" + addIndex + "个元素:" + obj);
                // 重置addIndex
                if (++addIndex == objects.length) {
                    addIndex = 0;
                }
                notFull.signal();

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }


        }

        // 移除数组头元素
        public Object remove() {
            lock.lock();
            try {
                while (count == 0) {
                    notFull.await();
                }
                Object o = objects[removeIndex];
                --count;
                System.out.println(Thread.currentThread().getName() + "移除第" + removeIndex + "个元素:" + o);
                objects[removeIndex] = null;
                if (++removeIndex == objects.length) {
                    removeIndex = 0;
                }
                notEmpty.signal();
                return o;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
        }
    }
}
